Add blueye logs command for listing and downloading dive logs - #220
Conversation
Surfaces the documented drone.logs workflow as a CLI built-in: - `blueye logs list` — table of the drone's binary logs (name, time, max depth, size, dive flag). - `blueye logs download [NAME ...] [--latest N | --all] [-o DIR]` — downloads .bez files; unknown names error listing what is available. - Bare `blueye logs` on a terminal opens an interactive picker: the table plus a checkbox multi-select and a destination prompt. The Prompter seam gains a checkbox method (questionary.checkbox interactively; a CliError naming the flags non-interactively). - Connects to the drone as an observer (connect_as_observer=True), so no control is taken and no SDK changes were needed; an unreachable drone fails with the friendly message and exit 1. The connection is released on exit. - The --drone-ip/--timeout parent parser and the failure-translation helper are lifted into commands/_common.py, now shared with the models command. Verified against the bench drone (list + `--latest 1` downloaded a real 2.5 MiB .bez) and an unreachable address. +14 tests (432 total); docs gain a "From the command line" section in the logs guide. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## jp-pino/bundle-model-cli #220 +/- ##
============================================================
+ Coverage 83.38% 84.34% +0.96%
============================================================
Files 32 36 +4
Lines 3214 3463 +249
============================================================
+ Hits 2680 2921 +241
- Misses 534 542 +8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
`blueye logs download ... --mcap` converts each downloaded .bez to a Foxglove-ready .mcap next to it; the interactive picker offers the same conversion after selecting logs. The converter (commands/logs/mcap.py) is adapted from examples/foxglove_bez_to_mcap.py: a first LogStream pass anchors the dive start time (last record's wall clock minus its monotonic delta, so logs where the clock was set mid-dive stay continuous), a second pass writes every protobuf message via mcap_protobuf.writer. mcap-protobuf-support joins the [cli] extra (and dev group); the --mcap path gates on it at runtime with the standard install guidance, so the rest of the logs command works without it. Verified against the bench drone: `blueye logs download --latest 1 --mcap` produced a valid 8.5 MB .mcap (217k messages, correct MCAP magic) from a real dive log. +6 tests incl. a real protobuf-built .bez round trip (438 total); the Foxglove doc gains a one-step download-and-convert tip. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a new blueye logs CLI command suite to list and download the drone’s binary dive logs, with an optional .bez → .mcap conversion flow intended for Foxglove, while reusing existing SDK log metadata and keeping the drone connection observer-only.
Changes:
- Introduces
blueye logs list/blueye logs download(plus interactiveblueye logs) backed by shared connection/error helpers. - Adds an MCAP conversion utility and wires it behind a runtime dependency gate (
--mcap). - Expands documentation and adds a comprehensive new hermetic test module for the logs command behavior.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Adds mcap-protobuf-support to locked optional CLI and dev dependency sets. |
| pyproject.toml | Adds mcap-protobuf-support to [project.optional-dependencies].cli and dev. |
| tests/test_cli_logs_command.py | Adds end-to-end CLI tests for logs list/download, interactive flow, and MCAP conversion gating. |
| docs/logs/listing-and-downloading.md | Documents CLI-based log listing/downloading and interactive usage. |
| docs/logs/foxglove-bez-to-mcap.md | Adds a one-step CLI tip for downloading and converting to .mcap. |
| blueye/sdk/cli/prompts.py | Extends the prompting seam with a checkbox() prompt used by interactive log selection. |
| blueye/sdk/cli/commands/models/command.py | Refactors shared drone options and friendly error translation into _common. |
| blueye/sdk/cli/commands/logs/mcap.py | Adds .bez → .mcap converter (two-pass anchor strategy). |
| blueye/sdk/cli/commands/logs/command.py | Implements logs subcommands, interactive selection, download, and optional conversion. |
| blueye/sdk/cli/commands/logs/init.py | Registers the new logs command in the CLI command registry. |
| blueye/sdk/cli/commands/_common.py | New shared parser and error translation helpers for drone-talking commands. |
| blueye/sdk/cli/commands/init.py | Registers logs in the built-in command list ordering. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| def _select_downloads(args, log_files) -> list: | ||
| """Resolve the download selection from names/--latest/--all.""" | ||
| by_name = {log.name: log for log in log_files} | ||
| if args.all: | ||
| return list(log_files) | ||
| if args.latest is not None: | ||
| newest_first = sorted(log_files, key=lambda log: log.start_time, reverse=True) | ||
| return newest_first[: args.latest] | ||
| if args.names: | ||
| missing = [name for name in args.names if name not in by_name] | ||
| if missing: | ||
| available = ", ".join(sorted(by_name)) or "none" | ||
| raise CliError( | ||
| f"No log named {', '.join(missing)} on the drone (available: {available})." | ||
| ) | ||
| return [by_name[name] for name in args.names] | ||
| raise CliError("Nothing selected — pass log names, --latest N, or --all.") |
There was a problem hiding this comment.
Fixed in 44cdcad — the three selectors (names, --latest, --all) are now mutually exclusive and error with "Pass only one of log names, --latest N, or --all.", and --latest values below 1 are rejected instead of succeeding with an empty selection. Tests added for both.
# Conflicts: # pyproject.toml
Follow-up to the review changes merged from jp-pino/bundle-model-cli: - The logs command joins the consolidated docs/cli.md as a "Downloading dive logs" section (incl. --mcap); the logs guide and the Foxglove doc keep short pointers to the CLI page. - The quick start gains a "Try the command line interface" section showcasing `blueye logs download --latest 1` with links to the CLI page. - With rich/questionary in the core dependencies, the logs command declares requires=() (the --mcap path keeps its runtime gate); mcap-protobuf-support stays in the [cli] extra alongside onnx. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…example Follow-ups on the logs command: - New `blueye logs convert <file.bez ...> [-o DIR]` converts already-downloaded logs to .mcap without touching the drone (dispatch happens before the observer connection; same runtime mcap gate). - Interactive view redesigned: instead of printing the full table and then a duplicate checkbox list, it is now a single scrollable multi-select table (each choice is a column-aligned row under one header line) with type-to-filter (questionary use_search_filter), sorted descending alphabetically — newest logs first. An explicit instruction string works around questionary 2.1.1 showing <ctrl-a> for both toggle-all and invert (the real invert binding is ctrl-i/tab). - New --dives-only / --since / --until filters shared by list, download, and the interactive view; list output is sorted descending too. - The example converter (examples/foxglove_bez_to_mcap.py) is retired in favor of the first-party command: the Foxglove doc now walks through `blueye logs download --latest 1 --mcap` and `blueye logs convert`, and mcap-protobuf-support leaves the [examples] extra (its only consumer). Verified against the bench drone: filtered+sorted list, download, and a local `blueye logs convert` producing a valid .mcap (1.19M messages). +13 tests (451 total); docs build clean with the embed removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Works around a second questionary 2.1.1 search-filter bug (user-found): with a filter active, ctrl-a (toggle all) and ctrl-i/tab (invert) operated on every choice — selecting files not even in view — because questionary's handlers iterate ic.choices instead of ic.filtered_choices. The prompter now replaces both key bindings on the constructed prompt with versions scoped to the visible rows: toggle-all selects/deselects only what the filter shows, invert flips only the visible rows, and selections hidden by the filter are left untouched. The selection logic lives in pure helpers (_toggle_all_visible, _invert_visible) with unit tests, plus an integration test asserting the rebinding attaches to a real questionary prompt. +5 tests (456 total). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
prompt_toolkit raises NoConsoleScreenBufferError when questionary builds a prompt without a console; the test now uses a pipe input and DummyOutput app session. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses the Copilot review on #220: names/--latest/--all are now mutually exclusive instead of silently prioritized, and --latest must be at least 1 rather than succeeding with an empty selection. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
Adds a
blueye logsbuilt-in to the CLI (stacked on #219's command registry), surfacing the documenteddrone.logsworkflow from the terminal — including first-party conversion to Foxglove's.mcapformat, which retires the example converter script.blueye logs list— table of the drone's binary logs (name, time, max depth, size, dive flag), sorted descending alphabetically (newest first).blueye logs download [NAME ...] [--latest N | --all] [-o DIR]— downloads.bezfiles; unknown names error with the available names listed.--mcap— converts each downloaded log to a Foxglove-ready.mcapnext to the.bez. The converter (commands/logs/mcap.py) is adapted from the former example script, keeping its two-pass timestamp anchoring so logs where the drone's clock was set mid-dive still get a continuous timeline.blueye logs convert <file.bez ...> [-o DIR]— converts already-downloaded logs locally, no drone connection needed.--dives-only,--since YYYY-MM-DD,--until YYYY-MM-DDshared bylist,download, and the interactive view.blueye logson a terminal opens a single scrollable multi-select table (column-aligned rows under one header, type-to-filter via questionary's search filter, sorted newest first), then asks for a destination and whether to convert to.mcap. An explicit instruction string works around questionary 2.1.1 displaying<ctrl-a>for both toggle-all and invert (the real invert binding is ctrl-i/tab).connect_as_observer=True(no control taken, released on exit);convertnever connects. Unreachable drones fail gracefully with exit 1.--drone-ip/--timeoutparent parser and the transport-failure→CliErrortranslation live incommands/_common.py, shared with themodelscommand.examples/foxglove_bez_to_mcap.pyis deleted in favor of the first-party command;mcap-protobuf-supportleaves the[examples]extra (stays in[cli]), and the Foxglove guide now walks through the CLI instead of cloning the repo.Testing
use_search_filterwiring) — 451 total, black clean, mkdocs build clean.list;download --latest 1 --mcapproduced a valid.mcap; localblueye logs converton a downloaded.bezproduced a valid 1.19M-message.mcap(MCAP magic checked); unreachable-address runs exit 1 with the friendly message.Docs
docs/cli.mdgains the logs section (incl.convertand the filter flags), the quick start showsblueye logs download --latest 1, the logs guide links the CLI page, and the Foxglove guide is rewritten around the one-step CLI flow.🤖 Generated with Claude Code